package com.koron.css2.common.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.koron.css2.ApplicationConfig;
import com.koron.css2.common.dto.AttachmentDto;
import com.koron.css2.serviceManage.bean.RequestBean;
import com.koron.css2.serviceManage.bean.UserInfoBean;
import com.koron.util.DBSourceUtils;
import com.koron.util.FileUtils;
import com.koron.util.Tools;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.koron.ebs.mybatis.ADOConnection;
import org.koron.ebs.mybatis.SessionFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * 大批量 EXCEL 分页导出
 */
public abstract class AbstractExportBigExcel extends AbstractExportExcel {

    private static Logger logger = LogManager.getLogger(AbstractExportBigExcel.class);

    private static final int BATCH_COUNT = 40000;

    public abstract AttachmentDto getAttachment(SessionFactory factory, UserInfoBean userInfo, RequestBean req);

    public abstract long count(SessionFactory factory, UserInfoBean userInfo, RequestBean req);

    public abstract List getData(SessionFactory factory, UserInfoBean userInfo, RequestBean req, int page, int pageSize);

    public HttpEntity<?> exportExcel(UserInfoBean userInfo, RequestBean req){
        String dbSource = DBSourceUtils.getDbEnv(userInfo.getCurWaterCode());
        HttpEntity<?> httpEntity = ADOConnection.runTask(dbSource, factory -> exportExcel(factory, userInfo, req), HttpEntity.class);
        return httpEntity;
    }

    private HttpEntity<?> exportExcel(SessionFactory factory, UserInfoBean userInfo, RequestBean req){
        try {
            long begin = System.currentTimeMillis();
            long iCount = count(factory, userInfo, req);
            List<File> files = new ArrayList<File>();
            int size = (int) iCount / BATCH_COUNT;
            int remainder = (int) iCount % BATCH_COUNT; // 余数
            if(remainder > 0) {
                size = size + 1;
            }
            AttachmentDto attachmentDto = getAttachment(factory, userInfo, req);
            if (iCount <= BATCH_COUNT){
                List data = getData(factory, userInfo, req, 1, BATCH_COUNT);
                File templateFile = templateExcelFile(attachmentDto.getExpType(), attachmentDto.getFileTemplate(), ApplicationConfig.getUploadTempDir(), data, attachmentDto.getFilename());
                try(InputStream inputStream = new FileInputStream(templateFile)){
                    HttpHeaders header = new HttpHeaders();
                    header.add("Content-Disposition", "attachment; filename=" + new String(attachmentDto.getFilename().getBytes("utf8"), "ISO8859-1") + ".xlsx");
                    header.add("Content-Type", "application/octet-stream");
                    return new ResponseEntity<byte[]>(IOUtils.toByteArray(inputStream), header, HttpStatus.CREATED);
                }
            }else {
                File zipFolder = FileUtils.templateZipFolder(Tools.getObjectId());
                for(int i = 1; i <= size; i++) {
                    List data = getData(factory, userInfo, req, i, BATCH_COUNT);
                    files.add(templateExcelFile(attachmentDto.getExpType(), attachmentDto.getFileTemplate(), zipFolder.getPath(), data, "report(" + i + ")"));
                }
                File zipFile = FileUtils.templateZipFile(zipFolder.getName() + ".zip");
                zipFile = FileUtils.toZip(files, zipFile);
                try(InputStream input = new FileInputStream(zipFile)){
                    long end = System.currentTimeMillis() - begin;
                    logger.info("导出{}行，共耗时：{}", iCount, (end / 1000));
                    HttpHeaders header = new HttpHeaders();
                    header.add("Content-Disposition", "attachment; filename=" + new String(attachmentDto.getFilename().getBytes("utf8"), "ISO8859-1") + ".zip");
                    header.add("Content-Type", "application/octet-stream");
                    return new ResponseEntity<byte[]>(IOUtils.toByteArray(input), header, HttpStatus.CREATED);
                }
            }
        }catch (Exception ex){
            ex.printStackTrace();
            logger.error("导出失败：" + ex);
        }
        return new ResponseEntity<String>("导出失败", HttpStatus.OK);
    }

    private File templateExcelFile(AttachmentDto.ExpType expType, String templateFileName, String templateFilePath, List data, String filename) {
        try {
            String filepath = templateFilePath + File.separator + filename + ".xlsx";
            File templateFile = new File(filepath);
            if(!templateFile.exists()) {
                templateFile.createNewFile();
            }
            try(OutputStream output = new FileOutputStream(templateFile)){
                if (expType != null && expType == AttachmentDto.ExpType.WRITE_TYPE){
                    List<String> headArray = (List<String>)data.get(0);
                    List<List<String>> list = new ArrayList<List<String>>();
                    for (String head : headArray) {
                        List<String> headList = new ArrayList<String>();
                        headList.add(head);
                        list.add(headList);
                    }
                    final ExcelWriter write = EasyExcel.write(output)
                            .head(list)
                            .registerWriteHandler(new ExcelWidthStyleStrategy())
                            .registerWriteHandler(ExcelWidthStyleStrategy.getStyleStrategy())
                            .build();
                    final WriteSheet sheet = EasyExcel.writerSheet(0, "sheet1").build();
                    data.remove(0);
                    write.write(data, sheet);
                    write.finish();
                }else {
                    URL url = AbstractExportBigExcel.class.getClassLoader().getResource("template/excel/" + templateFileName);
                    if(url == null) {
                        throw new NullPointerException("类路径下不存在模板文件");
                    }
                    File excelFile = new File(url.getPath());
                    final ExcelWriter write = EasyExcel.write(output)
                            .withTemplate(excelFile)
                            .registerWriteHandler(new ExcelWidthStyleStrategy())
                            .registerWriteHandler(ExcelWidthStyleStrategy.getStyleStrategy())
                            .build();
                    final WriteSheet sheet = EasyExcel.writerSheet(0, "sheet1").build();
                    write.fill(data, sheet);
                    write.finish();
                }
            }
            return templateFile;
        }catch (Exception ex){
            throw new RuntimeException(ex);
        }
    }
}
